﻿using System;
using System.Collections.Generic;
using System.Linq;
using Extensions;
using Natural.Managers;
using Natural.Model;
using Natural.Providers;

namespace Natural.Helpers
{
    public sealed class Solver
    {
        public const string EXECUTE = "Execute";
        public const string NOTIFY = "Notify";
        public const string EVALUATE = "Evaluate";
                
        public static object Solve(string key,string strategy=null)
        {
            ImplementInfo strategyInfo = null;
            ProviderInfo providers = Runtime.Info.Providers[key.ToUpper()];
            if (!string.IsNullOrEmpty(strategy))
                strategyInfo = providers.Strategies.Where(p => p.Name == strategy).FirstOrDefault();
            else if (providers.Defualt != null)
                strategyInfo = providers.Defualt;
            else
                strategyInfo = providers.Strategies.OrderByDescending(p => p.Priority).FirstOrDefault();

            if (strategyInfo.Instance == null)
            {
                Type type = Type.GetType(strategyInfo.TypeName);
                object instance = Activator.CreateInstance(type);
                foreach (ImplementInfo decoratorInfo in providers.Decorators.OrderBy(p => p.Priority))
                {
                    Type decoratorType = Type.GetType(decoratorInfo.TypeName);
                    instance = Activator.CreateInstance(decoratorType, instance);
                }
                strategyInfo.Instance = instance;
            }
            return strategyInfo.Instance;
        }

        public static void Build(ActionInfo actionInfo)
        {
            foreach (ImplementInfo strategy in actionInfo.Strategies)
            {
                if (strategy.Instance == null)
                { 
                    strategy.Instance = Create(strategy);
                    strategy.Instance = Decorate(strategy.Instance,actionInfo.Decorators);
                }
            }
            foreach (ImplementInfo observer in actionInfo.Observers)
                 if (observer.Instance == null)
                     observer.Instance = Create(observer);
        }

        public static ActionInfo ActionInfo(string action)
        {
            return ActionInfo(null, action);
        }
        public static ActionInfo ActionInfo(string entity,string action)
        {
            return !string.IsNullOrEmpty(entity)
                   ? Solver.EntityInfo(entity).Actions[action.ToUpper()]
                   : Runtime.Info.Actions[action.ToUpper()];
        }
        public static EntityInfo EntityInfo(string name)
        {
            string[] entityPath = name.Split('.');

            EntityInfo info = Runtime.Info.Entities[entityPath[0].ToUpper()];
            Type type = Type.GetType(info.TypeName);
            for (int i = 1; i < entityPath.Length; i++)
            {
                string propertyName = entityPath[i];
                type = type.GetProperty(propertyName).PropertyType;
                info = Runtime.Info.Entities[type.Name.ToUpper()];
            }
            return info;
        }



        public static ImplementInfo Strategy(List<ImplementInfo> strategies, object[] parameters)
        {
            if (strategies == null || strategies.Count == 0) return null;
            foreach (ImplementInfo strategyInfo in strategies.OrderBy(p => p.Priority))
            {
                if (strategyInfo.Instance == null)
                    strategyInfo.Instance = Create(strategyInfo);                
                //Si no existe el metodo Evaluate retorna la estrategia sin evaluarla
                if (strategyInfo.Instance.GetType().GetMethods().Where(p => p.Name == "Evaluate").Count() ==0)
                    return strategyInfo;
                // si contiene el metodo evaluate evalua la estrategia
                if ((bool)ReflectionHelper.Invoke(strategyInfo.Instance, "Evaluate", parameters))
                        return strategyInfo;
                
            }
            return null;
        }
        public static object Decorate(object instance, List<ImplementInfo> decorators)
        {
            decorators.OrderBy(p => p.Priority).Foreach(p => instance = Create(p, instance));
            return instance;
        }        
        public static object Create(ImplementInfo info)
        {
            Type type = Type.GetType(info.TypeName);
            return Activator.CreateInstance(type);
        }
        public static object Create(ImplementInfo info,params object[] args)
        {
            Type type = Type.GetType(info.TypeName);
            return Activator.CreateInstance(type, args);
        }
        public static object[] Parameters(ActionInfo actionInfo, string[] parameters)
        {
            if(parameters == null)return new object[0];
            Build(actionInfo);
            System.Reflection.ParameterInfo[] parametersInfo=ReflectionHelper.GetMethod(actionInfo.Defualt.Instance.GetType(), "Execute").GetParameters();
            object[] _parameters = new object[parametersInfo.Length-1];
            foreach (System.Reflection.ParameterInfo parameterInfo in parametersInfo)
            {
                if (parameterInfo.Position == 0) continue;
                int i = parameterInfo.Position-1;
                if(parameters.Length > i)
                    _parameters[i] = Serializer.Deserialize(parameters[i], parameterInfo.ParameterType);
                else
                    _parameters[i]=null;
            }
            return _parameters;
        }

    }
}
